package com.clp.protocol.modbus_tcp.definition;

import com.clp.protocol.core.exception.EnumElemDoesNotExistException;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.ReqMbData;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.RespErrMbData;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.RespOkMbData;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.req.*;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.resp_ok.*;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.lang.reflect.InvocationTargetException;

/**
 * 功能码
 */
@Getter
@AllArgsConstructor
public enum FuncCode {
    /**
     * 01 读线圈状态
     */
    READ_COIL_STATUS((byte) 0x01, ReadCoilStatusReqMbData.class, ReadCoilStatusRespOkMbData.class, RespErrMbData.class, "读线圈状态"),
    /**
     * 02 读输入状态
     */
    READ_INPUT_STATUS((byte) 0x02, ReadInputStatusReqMbData.class, ReadInputStatusRespOkMbData.class, RespErrMbData.class, "读输入状态"),
    /**
     * 03 读保持型寄存器
     */
    READ_HOLDING_REGISTERS((byte) 0x03, ReadHoldRegsReqMbData.class, ReadHoldRegsRespOkMbData.class, RespErrMbData.class, "读保持型寄存器"),
    /**
     * 04 读输入型寄存器
     */
    READ_INPUT_REGISTERS((byte) 0x04, ReadInputRegsReqMbData.class, ReadInputRegsRespOkMbData.class, RespErrMbData.class, "读输入型寄存器"),
    /**
     * 05 强制单个线圈 / 设置单个继电器
     */
    FORCE_SINGLE_COIL((byte) 0x05, ForceSingleCoilReqMbData.class, ForceSingleCoilRespOkMbData.class, RespErrMbData.class, "强制单个线圈"),
    /**
     * 06 写单个寄存器
     */
    PRESET_SINGLE_REGISTER((byte) 0x06, PresetSingleRegReqMbData.class, PresetSingleRegRespOkMbData.class, RespErrMbData.class, "写单个寄存器"),
    /**
     * 15 强制多个线圈
     */
    FORCE_MULTIPLE_COILS((byte) 0x0F, ForceMultiCoilsReqMbData.class, ForceMultiCoilsRespOkMbData.class, RespErrMbData.class, "强制多个线圈"),
    /**
     * 16 写多个寄存器
     */
    FORCE_MULTIPLE_REGISTERS((byte) 0x10, ForceMultiRegsReqMbData.class, ForceMultiRegsRespOkMbData.class, RespErrMbData.class, "写多个寄存器"),
    /**
     * 读文件记录
     */
    READ_FILE_RECORD((byte) 0x14, null, null, RespErrMbData.class, "读文件记录"),
    /**
     * 写文件记录
     */
    WRITE_FILE_RECORD((byte) 0x15, null, null, RespErrMbData.class, "写文件记录"),
    /**
     * 读FIFO队列
     */
    READ_FIFO_QUEUE((byte) 0x18, null, null, RespErrMbData.class, "读FIFO队列");

    private final byte val;
    private final Class<? extends ReqMbData> reqDataClass;
    private final Class<? extends RespOkMbData> respOkDataClass;
    private final Class<? extends RespErrMbData> respErrDataClass;

    private final String desc;

    public ReqMbData newInvalidReqMbData() {
        if (reqDataClass == null) throw new RuntimeException("没有定义功能码 " + this + "(" + this.desc + ") 的请求数据类！");
        try {
            return reqDataClass.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("构造请求数据失败！");
    }

    public RespOkMbData newInvalidRespOkMbData() {
        if (respOkDataClass == null) throw new RuntimeException("没有定义功能码 " + this + "(" + this.desc + ") 的正确响应数据类！");
        try {
            return respOkDataClass.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("构造正确响应数据失败！");
    }

    public RespErrMbData newInvalidRespErrMbData() {
        if (respErrDataClass == null) throw new RuntimeException("没有定义功能码 " + this + "(" + this.desc + ") 的错误响应数据类！");
        try {
            return respErrDataClass.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        throw new RuntimeException("构造错误响应数据失败！");
    }

    public static FuncCode gain(byte val) {
        for (FuncCode funcCode : FuncCode.values()) {
            if (funcCode.val == val) {
                return funcCode;
            }
        }
        throw new EnumElemDoesNotExistException(FuncCode.class);
    }
}
